home *** CD-ROM | disk | FTP | other *** search
/ MacHack 1997 / MacHack 1997.toast / Hacks / Hacks ’97 / Finder Dungeon / source code / MoreFiles 1.4.6 / Sources / DirectoryCopy.c next >
Encoding:
C/C++ Source or Header  |  1997-06-28  |  20.5 KB  |  649 lines  |  [TEXT/MPS ]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    DirectoryCopy: A robust, general purpose directory copy routine.
  5. **
  6. **    by Jim Luther, Apple Developer Technical Support Emeritus
  7. **
  8. **    File:        DirectoryCopy.c
  9. **
  10. **    Copyright © 1992-1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  17. **    after having made changes. If you're going to re-distribute the source,
  18. **    we require that you make it clear in the source that the code was
  19. **    descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include <Types.h>
  23. #include <Errors.h>
  24. #include <Memory.h>
  25. #include <Files.h>
  26. #include <Script.h>
  27.  
  28. #define    __COMPILINGMOREFILES
  29.  
  30. #include "MoreFiles.h"
  31. #include "MoreFilesExtras.h"
  32. #include "MoreDesktopMgr.h"
  33. #include "FileCopy.h"
  34. #include "DirectoryCopy.h"
  35.  
  36. /*****************************************************************************/
  37.  
  38. /* local constants */
  39.  
  40. enum
  41. {
  42.     dirCopyBigCopyBuffSize  = 0x00004000,
  43.     dirCopyMinCopyBuffSize  = 0x00000200
  44. };
  45.  
  46.  
  47. /*****************************************************************************/
  48.  
  49. /* local data structures */
  50.  
  51. /* The EnumerateGlobals structure is used to minimize the amount of
  52. ** stack space used when recursively calling CopyLevel and to hold
  53. ** global information that might be needed at any time. */
  54.  
  55. #if PRAGMA_ALIGN_SUPPORTED
  56. #pragma options align=mac68k
  57. #endif
  58. struct EnumerateGlobals
  59. {
  60.     Ptr            copyBuffer;            /* pointer to buffer used for file copy operations */
  61.     long        bufferSize;            /* the size of the copy buffer */
  62.     CopyErrProcPtr errorHandler;    /* pointer to error handling function */
  63.     CopyFilterProcPtr copyFilterProc; /* pointer to filter function */
  64.     OSErr        error;                /* temporary holder of results - saves 2 bytes of stack each level */
  65.     Boolean        bailout;            /* set to true to by error handling function if fatal error */
  66.     short        destinationVRefNum;    /* the destination vRefNum */
  67.     Str63        itemName;            /* the name of the current item */
  68.     CInfoPBRec    myCPB;                /* the parameter block used for PBGetCatInfo calls */
  69. };
  70. #if PRAGMA_ALIGN_SUPPORTED
  71. #pragma options align=reset
  72. #endif
  73.  
  74. typedef struct EnumerateGlobals EnumerateGlobals;
  75. typedef EnumerateGlobals *EnumerateGlobalsPtr;
  76.  
  77.  
  78. /* The PreflightGlobals structure is used to minimize the amount of
  79. ** stack space used when recursively calling GetLevelSize and to hold
  80. ** global information that might be needed at any time. */
  81.  
  82. #if PRAGMA_ALIGN_SUPPORTED
  83. #pragma options align=mac68k
  84. #endif
  85. struct PreflightGlobals
  86. {
  87.     OSErr            result;                /* temporary holder of results - saves 2 bytes of stack each level */
  88.     Str63            itemName;            /* the name of the current item */
  89.     CInfoPBRec        myCPB;                /* the parameter block used for PBGetCatInfo calls */
  90.  
  91.     unsigned short    dstBlksPerAllocBlk;    /* the number of 512 byte blocks per allocation block on destination */
  92.     unsigned short    allocBlksNeeded;    /* the total number of allocation blocks needed  */
  93.  
  94.     unsigned short    tempBlocks;            /* temporary storage for calculations (save some stack space)  */
  95.     CopyFilterProcPtr copyFilterProc;    /* pointer to filter function */
  96. };
  97. #if PRAGMA_ALIGN_SUPPORTED
  98. #pragma options align=reset
  99. #endif
  100.  
  101. typedef struct PreflightGlobals PreflightGlobals;
  102. typedef PreflightGlobals *PreflightGlobalsPtr;
  103.  
  104. /*****************************************************************************/
  105.  
  106. /* static prototypes */
  107.  
  108. static    void    GetLevelSize(long currentDirID,
  109.                              PreflightGlobals *theGlobals);
  110.  
  111. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  112.                                             long srcDirID,
  113.                                             short dstVRefNum,
  114.                                             CopyFilterProcPtr copyFilterProc,
  115.                                             Boolean *spaceOK);
  116.  
  117. static    void    CopyLevel(long sourceDirID,
  118.                           long dstDirID,
  119.                           EnumerateGlobals *theGlobals);
  120.                           
  121. /*****************************************************************************/
  122.  
  123. static    void    GetLevelSize(long currentDirID,
  124.                              PreflightGlobals *theGlobals)
  125. {
  126.     short    index = 1;
  127.     
  128.     do
  129.     {
  130.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  131.         theGlobals->myCPB.dirInfo.ioDrDirID = currentDirID;    /* we need to do this every time */
  132.                                                             /* through, since GetCatInfo  */
  133.                                                             /* returns ioFlNum in this field */
  134.         theGlobals->result = PBGetCatInfoSync(&theGlobals->myCPB);
  135.         if ( theGlobals->result == noErr )
  136.         {
  137.             if ( (theGlobals->copyFilterProc == NULL) ||
  138.                  CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
  139.             {
  140.                 /* Either there's no filter proc OR the filter proc says to use this item */
  141.                 if ( (theGlobals->myCPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
  142.                 {
  143.                     /* we have a directory */
  144.                     
  145.                     GetLevelSize(theGlobals->myCPB.dirInfo.ioDrDirID, theGlobals); /* recurse */
  146.                     theGlobals->result = noErr; /* clear error return on way back */
  147.                 }
  148.                 else
  149.                 {
  150.                     /* we have a file - add its allocation blocks to allocBlksNeeded */
  151.                     
  152.                     /* get number of 512-byte blocks needed for data fork */
  153.                     if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen & 0x000001ff) != 0 )
  154.                     {
  155.                         theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9) + 1;
  156.                     }
  157.                     else
  158.                     {
  159.                         theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlLgLen >> 9;
  160.                     }
  161.                     /* now, calculate number of new allocation blocks needed */
  162.                     if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
  163.                     {
  164.                         theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
  165.                     }
  166.                     else
  167.                     {
  168.                         theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
  169.                     }
  170.                     
  171.                     /* get number of 512-byte blocks needed for resource fork */
  172.                     if ( ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen & 0x000001ff) != 0 )
  173.                     {
  174.                         theGlobals->tempBlocks = ((unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9) + 1;
  175.                     }
  176.                     else
  177.                     {
  178.                         theGlobals->tempBlocks = (unsigned long)theGlobals->myCPB.hFileInfo.ioFlRLgLen >> 9;
  179.                     }
  180.                     /* now, calculate number of new allocation blocks needed */
  181.                     if ( theGlobals->tempBlocks % theGlobals->dstBlksPerAllocBlk )
  182.                     {
  183.                         theGlobals->allocBlksNeeded += (theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk) + 1;
  184.                     }
  185.                     else
  186.                     {
  187.                         theGlobals->allocBlksNeeded += theGlobals->tempBlocks / theGlobals->dstBlksPerAllocBlk;
  188.                     }
  189.                 }
  190.             }
  191.         }
  192.         ++index;
  193.     } while ( theGlobals->result == noErr );
  194. }
  195.  
  196. /*****************************************************************************/
  197.  
  198. static    OSErr    PreflightDirectoryCopySpace(short srcVRefNum,
  199.                                             long srcDirID,
  200.                                             short dstVRefNum,
  201.                                             CopyFilterProcPtr copyFilterProc,
  202.                                             Boolean *spaceOK)
  203. {
  204.     HParamBlockRec pb;
  205.     OSErr error;
  206.     unsigned short dstFreeBlocks;
  207.     PreflightGlobals theGlobals;
  208.     
  209.     /* Get the number of 512 byte blocks per allocation block and */
  210.     /* number of free allocation blocks on the destination volume */
  211.     error = GetVolumeInfoNoName(NULL, dstVRefNum, &pb);
  212.     if ( error == noErr )
  213.     {
  214.         dstFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
  215.         
  216.         /* get allocation block size (always multiple of 512) and divide by 512
  217.           to get number of 512-byte blocks per allocation block */
  218.         theGlobals.dstBlksPerAllocBlk = ((unsigned long)pb.volumeParam.ioVAlBlkSiz >> 9);
  219.         
  220.         theGlobals.allocBlksNeeded = 0;
  221.  
  222.         theGlobals.myCPB.dirInfo.ioNamePtr = theGlobals.itemName;
  223.         theGlobals.myCPB.dirInfo.ioVRefNum = srcVRefNum;
  224.         
  225.         theGlobals.copyFilterProc = copyFilterProc;
  226.         
  227.         GetLevelSize(srcDirID, &theGlobals);
  228.         
  229.         /* Is there enough room on the destination volume for the source file? */
  230.         *spaceOK = (theGlobals.allocBlksNeeded <= dstFreeBlocks);
  231.     }
  232.     
  233.     return ( error );
  234. }
  235.  
  236. /*****************************************************************************/
  237.  
  238. static    void    CopyLevel(long sourceDirID,
  239.                           long dstDirID,
  240.                           EnumerateGlobals *theGlobals)
  241. {
  242.     long currentSrcDirID;
  243.     long newDirID;
  244.     short index = 1;
  245.     
  246.     do
  247.     {    
  248.         /* Get next source item at the current directory level */
  249.         
  250.         theGlobals->myCPB.dirInfo.ioFDirIndex = index;
  251.         theGlobals->myCPB.dirInfo.ioDrDirID = sourceDirID;
  252.         theGlobals->error = PBGetCatInfoSync(&theGlobals->myCPB);        
  253.  
  254.         if ( theGlobals->error == noErr )
  255.         {
  256.             if ( (theGlobals->copyFilterProc == NULL) ||
  257.                  CallCopyFilterProc(theGlobals->copyFilterProc, &theGlobals->myCPB) ) /* filter if filter proc was supplied */
  258.             {
  259.                 /* Either there's no filter proc OR the filter proc says to use this item */
  260.  
  261.                 /* We have an item.  Is it a file or directory? */
  262.                 if ( (theGlobals->myCPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
  263.                 {
  264.                     /* We have a directory */
  265.                     
  266.                     /* Create a new directory at the destination. No errors allowed! */
  267.                     theGlobals->error = DirCreate(theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName, &newDirID);
  268.                     if ( theGlobals->error == noErr )
  269.                     {
  270.                         /* Save the current source directory ID where we can get it when we come back
  271.                         ** from recursion land. */
  272.                         currentSrcDirID = theGlobals->myCPB.dirInfo.ioDrDirID;
  273.                         
  274.                         /* Dive again (copy the directory level we just found below this one) */
  275.                         CopyLevel(theGlobals->myCPB.dirInfo.ioDrDirID, newDirID, theGlobals);
  276.                         
  277.                         if ( !theGlobals->bailout )
  278.                         {
  279.                             /* Copy comment from old to new directory. */
  280.                             /* Ignore the result because we really don't care if it worked or not. */
  281.                             (void) DTCopyComment(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL);
  282.                             
  283.                             /* Copy directory attributes (dates, etc.) to newDirID. */
  284.                             /* No errors allowed */
  285.                             theGlobals->error = CopyFileMgrAttributes(theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL, theGlobals->destinationVRefNum, newDirID, NULL, true);
  286.                             
  287.                             /* handle any errors from CopyFileMgrAttributes */
  288.                             if ( theGlobals->error != noErr )
  289.                             {
  290.                                 if ( theGlobals->errorHandler != NULL )
  291.                                 {
  292.                                     theGlobals->bailout =  CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, copyDirFMAttributesOp,
  293.                                                             theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  294.                                                             theGlobals->destinationVRefNum, newDirID, NULL);
  295.                                 }
  296.                                 else
  297.                                 {
  298.                                     /* If you don't handle the errors with an error handler, */
  299.                                     /* then the copy stops here. */
  300.                                     theGlobals->bailout = true;
  301.                                 }
  302.                             }
  303.                         }
  304.                     }
  305.                     else    /* error handling for DirCreate */
  306.                     {
  307.                         if ( theGlobals->errorHandler != NULL )
  308.                         {
  309.                             theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, dirCreateOp,
  310.                                                         theGlobals->myCPB.dirInfo.ioVRefNum, currentSrcDirID, NULL,
  311.                                                         theGlobals->destinationVRefNum, dstDirID, theGlobals->itemName);
  312.                         }
  313.                         else
  314.                         {
  315.                             /* If you don't handle the errors with an error handler, */
  316.                             /* then the copy stops here. */
  317.                             theGlobals->bailout = true;
  318.                         }
  319.                     }
  320.                     
  321.                     if ( !theGlobals->bailout )
  322.                     {
  323.                         /* clear error return on way back if we aren't bailing out */
  324.                         theGlobals->error = noErr;
  325.                     }
  326.                 }
  327.                 else
  328.                 {
  329.                     /* We have a file, so copy it */
  330.                     
  331.                     theGlobals->error = FileCopy(theGlobals->myCPB.hFileInfo.ioVRefNum,
  332.                                                  theGlobals->myCPB.hFileInfo.ioFlParID,
  333.                                                  theGlobals->itemName,
  334.                                                  theGlobals->destinationVRefNum,
  335.                                                  dstDirID,
  336.                                                  NULL,
  337.                                                  NULL,
  338.                                                  theGlobals->copyBuffer,
  339.                                                  theGlobals->bufferSize,
  340.                                                  false);
  341.                             
  342.                     /* handle any errors from FileCopy */
  343.                     if ( theGlobals->error != noErr )
  344.                     {
  345.                         if ( theGlobals->errorHandler != NULL )
  346.                         {
  347.                             theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, fileCopyOp,
  348.                                                     theGlobals->myCPB.hFileInfo.ioVRefNum, theGlobals->myCPB.hFileInfo.ioFlParID, theGlobals->itemName,
  349.                                                     theGlobals->destinationVRefNum, dstDirID, NULL);
  350.                             if ( !theGlobals->bailout )
  351.                             {
  352.                                 /* If the CopyErrProc handled the problem, clear the error here */
  353.                                 theGlobals->error = noErr;
  354.                             }
  355.                         }
  356.                         else
  357.                         {
  358.                             /* If you don't handle the errors with an error handler, */
  359.                             /* then the copy stops here. */
  360.                             theGlobals->bailout = true;
  361.                         }
  362.                     }
  363.                 }
  364.             }
  365.         }
  366.         else
  367.         {    /* error handling for PBGetCatInfo */
  368.             /* it's normal to get a fnfErr when indexing; that only means you've hit the end of the directory */
  369.             if ( theGlobals->error != fnfErr )
  370.             {
  371.                 if ( theGlobals->errorHandler != NULL )
  372.                 { 
  373.                     theGlobals->bailout = CallCopyErrProc(theGlobals->errorHandler, theGlobals->error, getNextItemOp,
  374.                                             theGlobals->myCPB.dirInfo.ioVRefNum, sourceDirID, NULL, 0, 0, NULL);
  375.                     if ( !theGlobals->bailout )
  376.                     {
  377.                         /* If the CopyErrProc handled the problem, clear the error here */
  378.                         theGlobals->error = noErr;
  379.                     }
  380.                 }
  381.                 else
  382.                 {
  383.                     /* If you don't handle the errors with an error handler, */
  384.                     /* then the copy stops here. */
  385.                     theGlobals->bailout = true;
  386.                 }
  387.             }
  388.         }
  389.         ++index; /* prepare to get next item */
  390.     } while ( (theGlobals->error == noErr) && (!theGlobals->bailout) ); /* time to fall back a level? */
  391. }
  392.  
  393. /*****************************************************************************/
  394.  
  395. pascal    OSErr    FilteredDirectoryCopy(short srcVRefNum,
  396.                                       long srcDirID,
  397.                                       ConstStr255Param srcName,
  398.                                       short dstVRefNum,
  399.                                       long dstDirID,
  400.                                       ConstStr255Param dstName,
  401.                                       void *copyBufferPtr,
  402.                                       long copyBufferSize,
  403.                                       Boolean preflight,
  404.                                       CopyErrProcPtr copyErrHandler,
  405.                                       CopyFilterProcPtr copyFilterProc)
  406. {
  407.     EnumerateGlobals theGlobals;
  408.     Boolean    isDirectory;
  409.     OSErr    error;
  410.     Boolean ourCopyBuffer = false;
  411.     Str63    srcDirName, oldDiskName;
  412.     Boolean spaceOK;            
  413.     
  414.     /* Make sure a copy buffer is allocated. */
  415.     if ( copyBufferPtr == NULL )
  416.     {
  417.         /* The caller didn't supply a copy buffer so grab one from the application heap.
  418.         ** Try to get a big copy buffer, if we can't, try for a 512-byte buffer.
  419.         ** If 512 bytes aren't available, we're in trouble. */
  420.         copyBufferSize = dirCopyBigCopyBuffSize;
  421.         copyBufferPtr = NewPtr(copyBufferSize);
  422.         if ( copyBufferPtr == NULL )
  423.         {
  424.             copyBufferSize = dirCopyMinCopyBuffSize;
  425.             copyBufferPtr = NewPtr(copyBufferSize);
  426.             if ( copyBufferPtr == NULL )
  427.             {
  428.                 return ( memFullErr );
  429.             }
  430.         }
  431.         ourCopyBuffer = true;
  432.     }
  433.     
  434.     /* Get the real dirID where we're copying from and make sure it is a directory. */
  435.     error = GetDirectoryID(srcVRefNum, srcDirID, srcName, &srcDirID, &isDirectory);
  436.     if ( error != noErr )
  437.     {
  438.         goto ErrorExit;
  439.     }
  440.     if ( !isDirectory )
  441.     {
  442.         error = dirNFErr;
  443.         goto ErrorExit;
  444.     }
  445.     
  446.     /* Special case destination if it is the root parent directory. */
  447.     /* Since you can't create the root directory, this is needed if */
  448.     /* you want to copy a directory's content to a disk's root directory. */
  449.     if ( (dstDirID == fsRtParID) && (dstName == NULL) )
  450.     {
  451.         dstDirID = fsRtParID;
  452.         isDirectory = true;
  453.         error = noErr;
  454.     }
  455.     else
  456.     {
  457.         /*  Get the real dirID where we're going to put the copy and make sure it is a directory. */
  458.         error = GetDirectoryID(dstVRefNum, dstDirID, dstName, &dstDirID, &isDirectory);
  459.         if ( error != noErr )
  460.         {
  461.             goto ErrorExit;
  462.         }
  463.         if ( !isDirectory )
  464.         {
  465.             error =  dirNFErr;
  466.             goto ErrorExit;
  467.         }
  468.     }
  469.     
  470.     /* Get the real vRefNum of both the source and destination */
  471.     error = DetermineVRefNum(srcName, srcVRefNum, &srcVRefNum);
  472.     if ( error != noErr )
  473.     {
  474.         goto ErrorExit;
  475.     }
  476.     error = DetermineVRefNum(dstName, dstVRefNum, &dstVRefNum);
  477.     if ( error != noErr )
  478.     {
  479.         goto ErrorExit;
  480.     }
  481.     
  482.     if ( preflight )
  483.     {
  484.         error = PreflightDirectoryCopySpace(srcVRefNum, srcDirID, dstVRefNum, copyFilterProc, &spaceOK);
  485.         if ( error != noErr )
  486.         {
  487.             goto ErrorExit;
  488.         }
  489.         if ( !spaceOK )
  490.         {
  491.             error = dskFulErr; /* not enough room on destination */
  492.             goto ErrorExit;
  493.         }
  494.     }
  495.  
  496.     /* Create the new directory in the destination directory with the */
  497.     /* same name as the source directory. */
  498.     error = GetDirName(srcVRefNum, srcDirID, srcDirName);
  499.     if ( error != noErr )
  500.     {
  501.         goto ErrorExit;
  502.     }
  503.     
  504.     /* Again, special case destination if the destination is the */
  505.     /* root parent directory. This time, we'll rename the disk to */
  506.     /* the source directory name. */
  507.     if ( dstDirID == fsRtParID )
  508.     {
  509.         /* Get the current name of the destination disk */
  510.         error = GetDirName(dstVRefNum, fsRtDirID, oldDiskName);
  511.         if ( error == noErr )    
  512.         {
  513.             /* Shorten the name if it's too long to be the volume name */
  514.             TruncPString(srcDirName, srcDirName, 27);
  515.             
  516.             /* Rename the disk */
  517.             error = HRename(dstVRefNum, fsRtParID, oldDiskName, srcDirName);
  518.             /* and copy to the root directory */
  519.             dstDirID = fsRtDirID;
  520.         }
  521.     }
  522.     else
  523.     {
  524.         error = DirCreate(dstVRefNum, dstDirID, srcDirName, &dstDirID);
  525.     }
  526.     if ( error != noErr )
  527.     {
  528.         /* handle any errors from DirCreate */
  529.         if ( copyErrHandler != NULL )
  530.         {
  531.             if ( CallCopyErrProc(copyErrHandler, error, dirCreateOp,
  532.                                                     srcVRefNum, srcDirID, NULL,
  533.                                                     dstVRefNum, dstDirID, srcDirName) )
  534.             {
  535.                 goto ErrorExit;
  536.             }
  537.             else
  538.             {
  539.                 /* If the CopyErrProc handled the problem, clear the error here */
  540.                 /* and continue */
  541.                 error = noErr;
  542.             }
  543.         }
  544.         else
  545.         {
  546.             /* If you don't handle the errors with an error handler, */
  547.             /* then the copy stops here. */
  548.             goto ErrorExit;
  549.         }
  550.     }
  551.     
  552.     /* dstDirID is now the newly created directory! */
  553.         
  554.     /* Set up the globals we need to access from the recursive routine. */
  555.     theGlobals.copyBuffer = (Ptr)copyBufferPtr;
  556.     theGlobals.bufferSize = copyBufferSize;
  557.     theGlobals.destinationVRefNum = dstVRefNum; /* so we can get to it always */
  558.     theGlobals.myCPB.hFileInfo.ioNamePtr = (StringPtr)&theGlobals.itemName;
  559.     theGlobals.myCPB.hFileInfo.ioVRefNum = srcVRefNum;
  560.     theGlobals.errorHandler = copyErrHandler;
  561.     theGlobals.bailout = false;
  562.     theGlobals.copyFilterProc =  copyFilterProc;
  563.         
  564.     /* Here we go into recursion land... */
  565.     CopyLevel(srcDirID, dstDirID, &theGlobals);
  566.     error = theGlobals.error;    /* get the result */
  567.     
  568.     if ( !theGlobals.bailout )
  569.     {
  570.         /* Copy comment from source to destination directory. */
  571.         /* Ignore the result because we really don't care if it worked or not. */
  572.         (void) DTCopyComment(srcVRefNum, srcDirID, NULL, dstVRefNum, dstDirID, NULL);
  573.         
  574.         /* Copy the File Manager attributes */
  575.         error = CopyFileMgrAttributes(srcVRefNum, srcDirID, NULL,
  576.                     dstVRefNum, dstDirID, NULL, true);
  577.         
  578.         /* handle any errors from CopyFileMgrAttributes */
  579.         if ( (error != noErr) && (copyErrHandler != NULL) )
  580.         {
  581.             theGlobals.bailout = CallCopyErrProc(copyErrHandler, error, copyDirFMAttributesOp,
  582.                                                 srcVRefNum, srcDirID, NULL,
  583.                                                 dstVRefNum, dstDirID, NULL);
  584.         }
  585.     }
  586.  
  587. ErrorExit:
  588.     /* Get rid of the copy buffer if we allocated it. */
  589.     if ( ourCopyBuffer )
  590.     {
  591.         DisposePtr((Ptr)copyBufferPtr);
  592.     }
  593.  
  594.     return ( error );
  595. }
  596.  
  597. /*****************************************************************************/
  598.  
  599. pascal    OSErr    DirectoryCopy(short srcVRefNum,
  600.                               long srcDirID,
  601.                               ConstStr255Param srcName,
  602.                               short dstVRefNum,
  603.                               long dstDirID,
  604.                               ConstStr255Param dstName,
  605.                               void *copyBufferPtr,
  606.                               long copyBufferSize,
  607.                               Boolean preflight,
  608.                               CopyErrProcPtr copyErrHandler)
  609. {
  610.     return ( FilteredDirectoryCopy(srcVRefNum, srcDirID, srcName,
  611.                                    dstVRefNum, dstDirID, dstName,
  612.                                    copyBufferPtr, copyBufferSize, preflight,
  613.                                    copyErrHandler, NULL) );
  614. }
  615.  
  616. /*****************************************************************************/
  617.  
  618. pascal    OSErr    FSpFilteredDirectoryCopy(const FSSpec *srcSpec,
  619.                                          const FSSpec *dstSpec,
  620.                                          void *copyBufferPtr,
  621.                                          long copyBufferSize,
  622.                                          Boolean preflight,
  623.                                          CopyErrProcPtr copyErrHandler,
  624.                                          CopyFilterProcPtr copyFilterProc)
  625. {
  626.     return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  627.                                    dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
  628.                                    copyBufferPtr, copyBufferSize, preflight,
  629.                                    copyErrHandler, copyFilterProc) );
  630. }
  631.  
  632. /*****************************************************************************/
  633.  
  634. pascal    OSErr    FSpDirectoryCopy(const FSSpec *srcSpec,
  635.                                  const FSSpec *dstSpec,
  636.                                  void *copyBufferPtr,
  637.                                  long copyBufferSize,
  638.                                  Boolean preflight,
  639.                                  CopyErrProcPtr copyErrHandler)
  640. {
  641.     return ( FilteredDirectoryCopy(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
  642.                                    dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
  643.                                    copyBufferPtr, copyBufferSize, preflight,
  644.                                    copyErrHandler, NULL) );
  645. }
  646.  
  647. /*****************************************************************************/
  648.  
  649.